package org.oep.jmingle;

import java.io.IOException;

import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.game.Sprite;

import org.oep.game.Actor;
import org.oep.game.ImageStore;
import org.oep.game.Point;

/**
 * Class Minglet just adds a few extra fields to the Actor class. These were
 * originally included in the Actor class, but they made Actor less general.
 * So, we define them here.
 * @author pmkilgo
 *
 */
public class Minglet extends Actor {
	private String id;
	private String owner;
	private String name;
	private long lastSeen;
	private boolean collidable = true;
	private boolean friend = false;
	private boolean mingled = false;
	
	public static final int SPEED = 2;
	
	// TODO: Make these animations
	public static final int SMILING = Actor.CUSTOM_STATE;
	public static final int ECSTATIC = Actor.CUSTOM_STATE + 1;
	public static final int BORED = Actor.CUSTOM_STATE + 2;
	public static final int FALLING = Actor.CUSTOM_STATE + 3;
	
	/*
	 * Let's build a state machine shall we?
	 * 
	 * AI_IDLE - We're waiting around to pick up a target
	 * 	  links to when target is acquired
	 * AI_WALKING - We have a target and we're walking to him
	 *    links to when reached target
	 * AI_MINGLING - We are mingling with the target
	 *    links to AI_IDLE when done
	 *    
	 *  More notes:
	 *     To acquire a target, it will be assigned by a parent object externally.
	 *     Each iteration of a game loop should call a public method that performs
	 *       
	 */
	
	public static final int AI_IDLE = 0;
	public static final int AI_WALKING = 1;
	public static final int AI_MINGLING = 2;
	
	public static final int MINGLE_LIFE = 50;
	
	private int AI_STATE = Minglet.AI_IDLE;
	private int AI_COUNTER = 0;
	
	private Minglet target;
	private Point navPoint;
	
	private boolean mingling = false;
	
	// The constructor is JMingle-specific code
	public Minglet(String hash, String owner) {
		this(hash, owner, -1);
	}
	
	public Minglet(String hash, String owner, long time) {
		this.id = hash;
		this.owner = owner;
		this.name = Utils.makeName( hash );
		lastSeen = time;
	}
	

	/**
	 * This method will load an image from the file and recolor it to make it more
	 * individual according to the device address.
	 */
	public void addAnimation(int state, String animKey, String spriteSheet, int frameX, int frameY) {
		/*
		 * Basic algorithm:
		 * 	1. Load the image, if failed, just make it an X
		 *  2. Recolor the images based on procedurally generated algorithm
		 *  3. Make two sprites and mirror one across the Y-axis
		 *  4. If we are using a "LEFT" or "RIGHT" animation, use the mirrored one
		 *  	to provide a default for the opposite direction.
		 */
		Image im;
		try {
			im = ImageStore.getInstance().get(spriteSheet);
			im = Utils.customizeMinglet(im, id);
		} catch(IOException e) {
			im = Image.createImage(frameX, frameY);
			Graphics g = im.getGraphics();
			g.setColor(0xFF0000);
			g.drawLine(0, 0, frameX, frameY);
			g.drawLine(frameX, 0, 0, frameY);
		}
		Sprite s = new Sprite(im, frameX, frameY);
		Sprite r = new Sprite(im, frameX, frameY);
		r.setTransform(Sprite.TRANS_MIRROR);
		
		if(!heightSpecified) {
			height = s.getHeight();
			heightSpecified = true;
		}
		
		if(!widthSpecified) {
			width = s.getWidth();
			widthSpecified = true;
		}
		
		putSprite(animKey, s);
		
		// Decide if we need to put the mirrored sprite
		if(state == Actor.WALK_LEFT && getSprite(Actor.WALK_RIGHT) == null) {
			putSprite(Actor.KEY_WALK_RIGHT, r);
		}
		else if(state == Actor.WALK_RIGHT && getSprite(Actor.WALK_LEFT) == null) {
			putSprite(Actor.KEY_WALK_LEFT, r);
		}
	}
	
	public void doAI() {
		AI_COUNTER = Math.max(0, AI_COUNTER - 1);
		
		switch (AI_STATE) {
		case Minglet.AI_IDLE:
			if(!isPlaying(Actor.KEY_STAND)) {
				setAnimationState(Actor.KEY_STAND);
			}
			
			if(AI_COUNTER > 0) {
				break;
			}
			
			if(target != null) {
				AI_STATE = Minglet.AI_WALKING;
			}
			else if(navPoint != null) {
				AI_STATE = Minglet.AI_WALKING;
			}
			break;
		
		case Minglet.AI_WALKING:
			Point destination;
			// We should have a target. If we don't, go back to idle state
			if(target == null && navPoint == null) {
				AI_STATE = Minglet.AI_IDLE;
				break;
			}
			
			// If we're close enough, move to mingling. If not, try to move toward the target
			else if(target != null) {
				if(getX() < target.getX()) {
					destination = new Point(target.getX() - target.getWidth(), target.getY());
				}
				else {
					destination = new Point(target.getX() + target.getWidth(), target.getY());
				}
			}
			else {
				destination = navPoint;
			}
			
			if(position.distance(destination) <= 5) {
				AI_COUNTER = Minglet.MINGLE_LIFE + JMingle.RANDOM.nextInt(Minglet.MINGLE_LIFE);
				if(target != null) {
					AI_STATE = Minglet.AI_MINGLING;
					
					// HEY! LOOK AT ME!!
					if(!target.isTargeting(this)) {
						target.interrupt();
						target.setTarget(this);
					}
					
					System.out.println(this + " is mingling with " + target + ". LIFE: " + AI_COUNTER);
				}
				else {
					navPoint = null;
					AI_STATE = Minglet.AI_IDLE;
				}
			}
			else {
				moveToward(destination);
			}
			break;
	
		case Minglet.AI_MINGLING:			
			// TODO: Remove this little bit of code. This is filler until we get a real animation.
			if(!isPlaying(Minglet.KEY_STAND)) {
				setAnimationState(Minglet.KEY_STAND);
			}
			
			// The other minglet could leave...
			if(AI_COUNTER <= 0 || target.distanceTo(position) > target.getWidth() + 5) {
				if(AI_COUNTER > 0) System.out.println(this + " was abandoned.");
				else System.out.println(this + " finished mingling.");
				target = null;
				AI_COUNTER = Minglet.MINGLE_LIFE + JMingle.RANDOM.nextInt(Minglet.MINGLE_LIFE);
				AI_STATE = Minglet.AI_IDLE;
			}
			else {
				// TODO: Set animation state to "mingling"
			}
			break;
		}
	}
	
	public String getName() { return name; }
	public String getOwner() { return owner; }
	public String getAddress() { return id; }
	public long getLastSeen() { return lastSeen; }
	public int getAIState() { return AI_STATE; }
	
	
	public void setLastSeen(long time) {
		lastSeen = time;
	}
	
	public String toString() {
		return name;
	}
	
	public void setTarget(Minglet m) {
		if(m != this) target = m;
	}
	
	public boolean isTargeting(Minglet m) {
		return m == target;
	}
	
	public void goTo(int x, int y) {
		// We won't cry if we have mingled before.
		if(navPoint== null) navPoint = new Point(x,y);
	}
	
	/**
	 * Stop everything that this minglet is doing and immediately return to an idle state.
	 */
	public void interrupt() {
		navPoint = null;
		target = null;
		AI_STATE = Minglet.AI_IDLE;
		AI_COUNTER = 0;
	}
	
	
	/**
	 * Immediately enter the mingle state and mingle with a specified minglet
	 * @param a the minglet to mingle with
	 */
	public void forceMingle(Minglet a) {
		interrupt();
		AI_COUNTER = Minglet.MINGLE_LIFE + JMingle.RANDOM.nextInt(Minglet.MINGLE_LIFE);
		AI_STATE = Minglet.AI_MINGLING;
		target = a;
	}
	
	
	/**
	 * Returns true if the minglet has nothing to do
	 * @return true if idle, false if not
	 */
	public boolean isUntasked() {
		return navPoint == null && target == null && AI_COUNTER <= 0;
	}
	
	private void moveToward(Point p) {
		boolean stateSet = false;
		if(position.x < p.x && position.x + Minglet.SPEED <= p.x) {
			position.x += Minglet.SPEED;
			if(!stateSet) { setAnimationState(Minglet.KEY_WALK_RIGHT); stateSet = true; }
		}
		else if(position.x > p.x && position.x - Minglet.SPEED >= p.x) {
			position.x -= Minglet.SPEED;
			if(!stateSet) { setAnimationState(Minglet.KEY_WALK_LEFT); stateSet = true; }
		}
		
		if(position.y < p.y && position.y + Minglet.SPEED <= p.y) {
			position.y += Minglet.SPEED;
			if(!stateSet) { setAnimationState(Minglet.KEY_WALK_DOWN); stateSet = true; }
		}
		else if(position.y > p.y && position.y - Minglet.SPEED >= p.y) {
			position.y -= Minglet.SPEED;
			if(!stateSet) { setAnimationState(Minglet.KEY_WALK_UP); stateSet = true; }
		}
	}

	public boolean collidesWith(Minglet a) {
		if(a.distanceTo(position) < getWidth() && collidable && a.isCollidable()) return true;
		return false;
	}
	
	public boolean isCollidable() {
		return collidable;
	}
	
	public void setCollidable(boolean b) {
		collidable = b;
	}

	
	public void setFriend(boolean b) {
		friend = b;
	}
	
	public boolean isFriend() {
		return friend;
	}
	
	public void setMingled(boolean b) {
		mingled = b;
	}
	
	public boolean hasMingled() {
		return mingled;
	}

	
	public void setMingling(boolean b) {
		mingling = b;
	}
	
	public boolean getMingling() { return mingling; }
	
	public Minglet getTarget() {
		return target;
	}
	

	public static final String
		KEY_FALL = "FALL",
		KEY_SMILE = "SMILE";

}
